package com.softaria.windows.widgets;

import com.allen_sauer.gwt.dnd.client.AbstractDragController;
import com.allen_sauer.gwt.dnd.client.util.DOMUtil;
import com.allen_sauer.gwt.dnd.client.util.Location;
import com.allen_sauer.gwt.dnd.client.util.WidgetLocation;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Widget;

public class ResizeDragController extends AbstractDragController {

  public interface ResizeCompleteListener {
    void notifyResizeStarted();

    void notifyResizeCompleted();
  }

  public static final int NORTH = 0;
  public static final int NORTH_EAST = 1;
  public static final int NORTH_WEST = 2;
  public static final int EAST = 3;
  public static final int WEST = 4;
  public static final int SOUTH_EAST = 5;
  public static final int SOUTH_WEST = 6;
  public static final int SOUTH = 7;

  // This value can't be too small. It must be at least (heightOfTopBorder+
  // heightOfBottomBorder+minimal height of vertical scrollbar)
  // 50 looks enough, but do not replace it with e.g. 30
  private static final int MINIMUM_SIZE = 50;

  private Widget resizeTarget;
  private int direction;
  private ResizeCompleteListener resizeListener;
  private int dropTargetClientWidth;
  private int dropTargetClientHeight;
  private Widget draggable;

  public ResizeDragController(AbsolutePanel boundaryPanel, Widget resizeTarget, int dir,
      ResizeCompleteListener resizeListener) {
    super(boundaryPanel);
    this.resizeListener = resizeListener;
    this.resizeTarget = resizeTarget;
    this.direction = dir;

  }

  @Override
  public void dragMove() {

    // Calculating boundary offset (exactly as it is done in "original"
    // PickupDragController)
    Location widgetLocation = new WidgetLocation(context.boundaryPanel, null);
    int boundaryOffsetX = widgetLocation.getLeft()
        + DOMUtil.getBorderLeft(context.boundaryPanel.getElement());
    int boundaryOffsetY = widgetLocation.getTop()
        + DOMUtil.getBorderTop(context.boundaryPanel.getElement());

    int desiredLeft = context.desiredDraggableX - boundaryOffsetX;
    int desiredTop = context.desiredDraggableY - boundaryOffsetY;

    if (getBehaviorConstrainedToBoundaryPanel()) {
      desiredLeft = Math.max(0, Math.min(desiredLeft, dropTargetClientWidth
          - context.draggable.getOffsetWidth()));
      desiredTop = Math.max(0, Math.min(desiredTop, dropTargetClientHeight
          - context.draggable.getOffsetHeight()));
    }

    WidgetLocation loc = new WidgetLocation(resizeTarget, getBoundaryPanel());

    switch (direction) {
    case NORTH: {
      north(desiredTop, loc, false);
      break;
    }
    case NORTH_EAST: {
      north(desiredTop, loc, false);
      east(desiredLeft, loc);
      break;
    }
    case EAST: {
      east(desiredLeft, loc);
      break;
    }
    case SOUTH_EAST: {
      south(desiredTop, loc);
      east(desiredLeft, loc);
      break;
    }
    case SOUTH: {
      south(desiredTop, loc);
      break;
    }
    case SOUTH_WEST: {
      south(desiredTop, loc);
      west(desiredLeft, loc, false);
      break;
    }
    case WEST: {
      west(desiredLeft, loc, false);
      break;
    }
    case NORTH_WEST: {
      int newY = north(desiredTop, loc, true);
      int newX = west(desiredLeft, loc, true);
      DOMUtil.fastSetElementPosition(resizeTarget.getElement(), newX, newY);
      break;
    }
    }
  }

  private int west(int newX, WidgetLocation loc, boolean skipMove) {
    // Here desired position of div equal to desired position of resizeWidget
    // int newX = context.desiredDraggableX - boundaryOffsetX;

    int deltaW = newX - loc.getLeft();
    int newW = resizeTarget.getOffsetWidth() - deltaW;

    if (newW < MINIMUM_SIZE) {

      newW = MINIMUM_SIZE;
      newX = resizeTarget.getOffsetWidth() - newW + loc.getLeft();

    }

    if (!skipMove) {
      DOMUtil.fastSetElementPosition(resizeTarget.getElement(), newX, loc.getTop());
    }
    resizeTarget.setWidth(newW + "px");

    return newX;

  }

  private void south(int desiredY, WidgetLocation loc) {
    // desired position of our small div, not resizedWidget
    // int desiredY = context.desiredDraggableY - boundaryOffsetY;

    // we must add width of our small <div> here, because desiredX is its left,
    // while we need its right boundary
    int newH = desiredY + context.draggable.getOffsetHeight() - loc.getTop();

    if (newH < MINIMUM_SIZE) {
      resizeTarget.setHeight(MINIMUM_SIZE + "px");
    } else {
      resizeTarget.setHeight(newH + "px");
    }

  }

  private void east(int desiredX, WidgetLocation loc) {
    // desired position of our small div, not resizedWidget
    // int desiredX = context.desiredDraggableX - boundaryOffsetX;

    // we must add width of our small <div> here, because desiredX is its left,
    // while we need its right boundary
    int newW = desiredX + context.draggable.getOffsetWidth() - loc.getLeft();

    if (newW < MINIMUM_SIZE) {
      resizeTarget.setWidth(MINIMUM_SIZE + "px");
    } else {
      resizeTarget.setWidth(newW + "px");
    }
  }

  private int north(int newY, WidgetLocation loc, boolean skipMove) {
    // Here desired position of div equal to desired position of resizeWidget
    // int newY = context.desiredDraggableY - boundaryOffsetY;

    int deltaH = newY - loc.getTop();
    int newH = resizeTarget.getOffsetHeight() - deltaH;

    if (newH < MINIMUM_SIZE) {
      newH = MINIMUM_SIZE;
      newY = resizeTarget.getOffsetHeight() - newH + loc.getTop();
    }
    if (!skipMove) {
      DOMUtil.fastSetElementPosition(resizeTarget.getElement(), loc.getLeft(), newY);
    }
    resizeTarget.setHeight(newH + "px");
    return newY;

  }

  @Override
  public void dragStart() {
    dropTargetClientWidth = DOMUtil.getClientWidth(getBoundaryPanel().getElement());
    dropTargetClientHeight = DOMUtil.getClientHeight(getBoundaryPanel().getElement());

    if (resizeListener != null) {
      resizeListener.notifyResizeStarted();
    }

    super.dragStart();
  }

  @Override
  public void dragEnd() {
    super.dragEnd();

    if (resizeListener != null) {
      resizeListener.notifyResizeCompleted();
    }

  }

  @Override
  public void makeDraggable(Widget draggable) {
    super.makeDraggable(draggable);
    this.draggable = draggable;
  }

  public Widget getDraggable() {
    return draggable;
  }

  public void enable() {
    makeDraggable(draggable);
  }

  public void disable() {
    makeNotDraggable(draggable);
  }

}
